package remato.domain.common.impl;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

import remato.common.domain.impl.base.DomainName;
import remato.common.domain.impl.internal.RequirementChild;
import remato.domain.common.Category;
import remato.domain.common.PropertyDef;
import remato.domain.common.PropertyValue;
import remato.domain.common.Requirement;
import remato.domain.common.Stakeholder;
import remato.domain.common.Status;

/**
 * 
 * @version $Id: RequirementImpl.java,v 1.1 2006-01-23 09:27:22 petterei Exp $
 */
public class RequirementImpl extends DomainName implements Requirement, RequirementChild {

	private Category category;
	/*
	 * Hessian does not handle Hashtable, thus transient. 
	 */
//	private transient Map<PropertyDef, PropertyValue> propertyDefToPropertyValue; // TODO: extend alogorithms to handle maps..
	private Set<PropertyValue> propertyValues;
	private Set<Stakeholder> stakeholders;
	private Set<Requirement> linkedToRequirements;
	private Integer status = new Integer(0);
	private Integer priority = new Integer (0);
	private Date lastUpdatedDate = null; // This date should be null when there is no update date!
	private Date submittedDate = null;   // This date should be null when there is no submitted date!
	private Stakeholder assignedTo;
	private Stakeholder reporterOrOwner;
	private String version; //should be not be set, untill further
	private Long businessId = null;
	private Status requirementStatus;
	
	@SuppressWarnings("unused")
	private static final transient Logger log = Logger.getLogger(RequirementImpl.class.getName());

	public RequirementImpl() {
		super();
		stakeholders = new HashSet<Stakeholder>();
		linkedToRequirements = new HashSet<Requirement>();
//		propertyDefToPropertyValue = new Hashtable<PropertyDef,PropertyValue>();
		propertyValues = new HashSet<PropertyValue>();
	}

	public Long getBusinessId() {
		return businessId;
	}

	public void setBusinessId(Long businessId) {
		this.businessId = businessId;
	}

	public Category getCategory() {
		return category;
	}

	public void setCategory(Category category) { 
		this.category = category;
	}

	/**
	 * @see remato.domain.common.Requirement#getLinkedToRequirements()
	 */
	public Set<Requirement> getLinkedToRequirements() {
		return new HashSet<Requirement>(linkedToRequirements);
	}

	public void setLinkedToRequirements(Set<Requirement> linkedToRequirements) {
		this.linkedToRequirements = linkedToRequirements;
	}

	public boolean addLinkedToRequirement(Requirement requirement) {
		boolean wasAdded = false;
		if (null != requirement && ! requirement.equals(this) ) { // not possible to link to itself..
			wasAdded =linkedToRequirements.add(requirement);
			if (wasAdded) {
					requirement.addLinkedToRequirement(this);
			}
		}
		return wasAdded;
	}

	public boolean removeLinkedToRequirement(Requirement requirement) {
		boolean wasRemoved = false;
		if (null != requirement) {
			wasRemoved = linkedToRequirements.remove(requirement);
			if (wasRemoved) {
					requirement.removeLinkedToRequirement(this);
			}
		}
		return wasRemoved;
	}

	public void setAssignedTo(Stakeholder newAssignedTo) {
		boolean equal = true;
		if (null != newAssignedTo && ! newAssignedTo.equals(assignedTo)) {
			equal = false;
			newAssignedTo.addAssignedRequirement(this);
		}
		if (! equal && null != assignedTo) {
			assignedTo.removeAssignedRequirement(this);
		}
		assignedTo = newAssignedTo;
	}
	
	public Stakeholder getAssignedTo() {
		return assignedTo;
	}

	public void setReporterOrOwner(Stakeholder newReporterOrOwner) {
		boolean equal = true;
		if (null != newReporterOrOwner && ! newReporterOrOwner.equals(reporterOrOwner)) {
			equal = false;
			newReporterOrOwner.addReporterOrOwnerToRequirement(this);
		}
		if (! equal && null != reporterOrOwner) {
			reporterOrOwner.removeReporterOrOwnerToRequirement(this);
		}
		reporterOrOwner = newReporterOrOwner;
	}

	public Stakeholder getReporterOrOwner() {
		return reporterOrOwner;
	}
	
	/**
	 * @see remato.domain.common.Requirement#getStakeholders()
	 */
	public Set<Stakeholder> getStakeholders() {
		return new HashSet<Stakeholder>(stakeholders);
	}

	public void setStakeholders(Set<Stakeholder> stakeholders) {
		this.stakeholders = stakeholders;
	}

	public boolean addStakeholder(Stakeholder stakeholder) {
		boolean wasAdded = false;
		if (! stakeholders.contains(stakeholder)) {
			stakeholder.addStakeholderToRequirement(this);
			wasAdded = stakeholders.add(stakeholder);
		}
		return wasAdded;
	}

	public boolean removeStakeholder(Stakeholder stakeholder) {
		stakeholder.removeStakeholderToRequirement(this);
		return stakeholders.remove(stakeholder);
	}

	public Set<PropertyValue> getPropertyValues() {
		return propertyValues;
	}

	public void setPropertyValues(Set<PropertyValue> newPropertyValues) {
//		propertyDefToPropertyValue = new Hashtable<PropertyDef, PropertyValue>();
//		for (PropertyValue value : newPropertyValues) {
//			internalPutPropertyValue(value);
//		}
		propertyValues = newPropertyValues; // this is last to ensure consistency
	}
	
//	private void internalPutPropertyValue(PropertyValue value) {
//		PropertyDef def = value.getPropertyDef();
//		if (null == def) {
//			throw new RematoException("PropertyValues must have a definition set.", new NullPointerException("The values definition was 'null'"));
//		}
//		propertyDefToPropertyValue.put(def, value);
//	}
	
	public PropertyValue putPropertyValue(PropertyValue value) {
//		internalPutPropertyValue(value);
		if ( propertyValues.add(value) ) {
			return null;
		}
		return value;
	}

	public PropertyValue getPropertyValue(PropertyDef def) {
//		return propertyDefToPropertyValue.get(def);
		return getPropertyValue(def.getName());
	}

	public PropertyValue getPropertyValue(String defLabel) {
//		for (PropertyValue val : propertyDefToPropertyValue.values()) {
		for (PropertyValue val : propertyValues) {
			if (defLabel.equals(val.getPropertyDef().getName())) {
				return val;
			}
		}
		return null;
	}
	
	public Date getLastUpdatedDate() {
		return lastUpdatedDate;
	}

	public void setLastUpdatedDate(Date lastUpdatedDate) {
		this.lastUpdatedDate = lastUpdatedDate;
	}

	public Integer getPriority() {
		return priority;
	}

	public void setPriority(Integer priority) {
		this.priority = priority;
	}

	public Integer getStatus() {
		return status;
	}

	public void setStatus(Integer status) {
		this.status = status;
	}

	public Date getSubmittedDate() {
		return submittedDate;
	}

	public void setSubmittedDate(Date submittedDate) {
		this.submittedDate = submittedDate;
	}

	public String getVersion() {
		return version;
	}

	public void setVersion(String version) {
		this.version = version;
	}

	public Status getRequirementStatus() {
		return requirementStatus;
	}

	public void setRequirementStatus(Status requirementStatus) {
		if ((null != this.requirementStatus) && (null != requirementStatus) && (!this.requirementStatus.getNextStatuses().contains(requirementStatus))) {
			log.warning("Invalid status set for requirement \"" + getName() + "\"(" + getBusinessId() + ")" + ": " + requirementStatus + ". Current status is " + this.requirementStatus + ".  Valid statuses are: " + this.requirementStatus.getNextStatuses());
//			I should not prevent an invalid status being being set, because this could lead to problems with Hibernate or similar ORM tools!
		}
		this.requirementStatus = requirementStatus;
	}

	
}
